{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Extended Unpacking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Let's see how we might split a list into it's first element, and \"everything else\" using slicing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l = [1, 2, 3, 4, 5, 6]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "a = l[0]\n",
    "b = l[1:]\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can even use unpacking to simplify this slightly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "a, b = l[0], l[1:]\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But we can use the **\\*** operator to achieve the same result:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "a, *b = l\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the **\\*** operator can only appear **once**!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Like standard unpacking, this extended unpacking will work with any iterable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With tuples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-10\n",
      "[5, 2, 100]\n"
     ]
    }
   ],
   "source": [
    "a, *b = -10, 5, 2, 100\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With strings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p\n",
      "['y', 't', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "a, *b = 'python'\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What about extracting the first, second, last elements and *the rest*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again we can use slicing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p\n",
      "y\n",
      "tho\n",
      "n\n"
     ]
    }
   ],
   "source": [
    "s = 'python'\n",
    "\n",
    "a, b, c, d = s[0], s[1], s[2:-1], s[-1]\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But we can just as easily do it this way using unpacking:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p\n",
      "y\n",
      "['t', 'h', 'o']\n",
      "n\n"
     ]
    }
   ],
   "source": [
    "a, b, *c, d = s\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see though, **c** is a list of characters, not a string.\n",
    "\n",
    "It that's a problem we can easily fix it this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['t', 'h', 'o']\n",
      "tho\n"
     ]
    }
   ],
   "source": [
    "print(c)\n",
    "c = ''.join(c)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also use unpacking on the right hand side of an assignment expression:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "l2 = [4, 5, 6]\n",
    "l = [*l1, *l2]\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 'A', 'B', 'C']\n"
     ]
    }
   ],
   "source": [
    "l1 = [1, 2, 3]\n",
    "s = 'ABC'\n",
    "l = [*l1, *s]\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This unpacking works with unordered types such as sets and dictionaries as well.\n",
    "\n",
    "The only thing is that it may not be very useful considering there is no particular ordering, so a first or last element has no real useful meaning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "s = {10, -99, 3, 'd'}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "3\n",
      "d\n",
      "-99\n"
     ]
    }
   ],
   "source": [
    "for c in s:\n",
    "    print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the order of the elements when we created the set was not retained!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "3\n",
      "['d', -99]\n"
     ]
    }
   ],
   "source": [
    "s = {10, -99, 3, 'd'}\n",
    "a, b, *c = s\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So unpacking this way is of limited use.\n",
    "\n",
    "However consider this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[10, 3, 'd', -99]\n"
     ]
    }
   ],
   "source": [
    "s = {10, -99, 3, 'd'}\n",
    "*a, = s\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At first blush, this doesn't look terribly exciting - we simply unpacked the set values into a list.\n",
    "\n",
    "But this is actually quite useful in both sets and dictionaries to combine things (although to be sure, there are alternative ways to do this as well - which we'll cover later in this course)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "s1 = {1, 2, 3}\n",
    "s2 = {3, 4, 5}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How can we combine both these sets into a single merged set?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "unsupported operand type(s) for +: 'set' and 'set'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-22-1659087814e1>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ms1\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0ms2\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'set' and 'set'"
     ]
    }
   ],
   "source": [
    "s1 + s2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Well, **+** doesn't work..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We could use the built-in method for unioning sets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on class set in module builtins:\n",
      "\n",
      "class set(object)\n",
      " |  set() -> new empty set object\n",
      " |  set(iterable) -> new set object\n",
      " |  \n",
      " |  Build an unordered collection of unique elements.\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __and__(self, value, /)\n",
      " |      Return self&value.\n",
      " |  \n",
      " |  __contains__(...)\n",
      " |      x.__contains__(y) <==> y in x.\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __iand__(self, value, /)\n",
      " |      Return self&=value.\n",
      " |  \n",
      " |  __init__(self, /, *args, **kwargs)\n",
      " |      Initialize self.  See help(type(self)) for accurate signature.\n",
      " |  \n",
      " |  __ior__(self, value, /)\n",
      " |      Return self|=value.\n",
      " |  \n",
      " |  __isub__(self, value, /)\n",
      " |      Return self-=value.\n",
      " |  \n",
      " |  __iter__(self, /)\n",
      " |      Implement iter(self).\n",
      " |  \n",
      " |  __ixor__(self, value, /)\n",
      " |      Return self^=value.\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __len__(self, /)\n",
      " |      Return len(self).\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __new__(*args, **kwargs) from builtins.type\n",
      " |      Create and return a new object.  See help(type) for accurate signature.\n",
      " |  \n",
      " |  __or__(self, value, /)\n",
      " |      Return self|value.\n",
      " |  \n",
      " |  __rand__(self, value, /)\n",
      " |      Return value&self.\n",
      " |  \n",
      " |  __reduce__(...)\n",
      " |      Return state information for pickling.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __ror__(self, value, /)\n",
      " |      Return value|self.\n",
      " |  \n",
      " |  __rsub__(self, value, /)\n",
      " |      Return value-self.\n",
      " |  \n",
      " |  __rxor__(self, value, /)\n",
      " |      Return value^self.\n",
      " |  \n",
      " |  __sizeof__(...)\n",
      " |      S.__sizeof__() -> size of S in memory, in bytes\n",
      " |  \n",
      " |  __sub__(self, value, /)\n",
      " |      Return self-value.\n",
      " |  \n",
      " |  __xor__(self, value, /)\n",
      " |      Return self^value.\n",
      " |  \n",
      " |  add(...)\n",
      " |      Add an element to a set.\n",
      " |      \n",
      " |      This has no effect if the element is already present.\n",
      " |  \n",
      " |  clear(...)\n",
      " |      Remove all elements from this set.\n",
      " |  \n",
      " |  copy(...)\n",
      " |      Return a shallow copy of a set.\n",
      " |  \n",
      " |  difference(...)\n",
      " |      Return the difference of two or more sets as a new set.\n",
      " |      \n",
      " |      (i.e. all elements that are in this set but not the others.)\n",
      " |  \n",
      " |  difference_update(...)\n",
      " |      Remove all elements of another set from this set.\n",
      " |  \n",
      " |  discard(...)\n",
      " |      Remove an element from a set if it is a member.\n",
      " |      \n",
      " |      If the element is not a member, do nothing.\n",
      " |  \n",
      " |  intersection(...)\n",
      " |      Return the intersection of two sets as a new set.\n",
      " |      \n",
      " |      (i.e. all elements that are in both sets.)\n",
      " |  \n",
      " |  intersection_update(...)\n",
      " |      Update a set with the intersection of itself and another.\n",
      " |  \n",
      " |  isdisjoint(...)\n",
      " |      Return True if two sets have a null intersection.\n",
      " |  \n",
      " |  issubset(...)\n",
      " |      Report whether another set contains this set.\n",
      " |  \n",
      " |  issuperset(...)\n",
      " |      Report whether this set contains another set.\n",
      " |  \n",
      " |  pop(...)\n",
      " |      Remove and return an arbitrary set element.\n",
      " |      Raises KeyError if the set is empty.\n",
      " |  \n",
      " |  remove(...)\n",
      " |      Remove an element from a set; it must be a member.\n",
      " |      \n",
      " |      If the element is not a member, raise a KeyError.\n",
      " |  \n",
      " |  symmetric_difference(...)\n",
      " |      Return the symmetric difference of two sets as a new set.\n",
      " |      \n",
      " |      (i.e. all elements that are in exactly one of the sets.)\n",
      " |  \n",
      " |  symmetric_difference_update(...)\n",
      " |      Update a set with the symmetric difference of itself and another.\n",
      " |  \n",
      " |  union(...)\n",
      " |      Return the union of sets as a new set.\n",
      " |      \n",
      " |      (i.e. all elements that are in either set.)\n",
      " |  \n",
      " |  update(...)\n",
      " |      Update a set with the union of itself and others.\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __hash__ = None\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(set)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1, 2, 3}\n",
      "{3, 4, 5}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{1, 2, 3, 4, 5}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(s1)\n",
    "print(s2)\n",
    "s1.union(s2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What about joining 4 different sets?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1, 2, 3, 4, 5, 6, 7, 8, 9}\n",
      "{1, 2, 3, 4, 5, 6, 7, 8, 9}\n"
     ]
    }
   ],
   "source": [
    "s1 = {1, 2, 3}\n",
    "s2 = {3, 4, 5}\n",
    "s3 = {5, 6, 7}\n",
    "s4 = {7, 8, 9}\n",
    "print(s1.union(s2).union(s3).union(s4))\n",
    "print(s1.union(s2, s3, s4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or we could use unpacking in this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1, 2, 3, 4, 5, 6, 7, 8, 9}"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{*s1, *s2, *s3, *s4}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What we did here was to unpack each set directly into another set!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same works for dictionaries - just remember that **\\*** for dictionaries unpacks the keys only."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['key1', 'key2', 'key2', 'key3']"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d1 = {'key1': 1, 'key2': 2}\n",
    "d2 = {'key2': 3, 'key3': 3}\n",
    "[*d1, *d2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, is there anything to unpack the key-value pairs for dictionaries instead of just the keys?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yes - we can use the **\\*\\*** operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'key1': 1, 'key2': 3, 'key3': 3}"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d1 = {'key1': 1, 'key2': 2}\n",
    "d2 = {'key2': 3, 'key3': 3}\n",
    "\n",
    "{**d1, **d2}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice what happened to the value of **key2**. The value for the second occurrence of **key2** was retained (overwritten)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact, if we write the unpacking reversing the order of d1 and d2:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'key1': 1, 'key2': 2, 'key3': 3}"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{**d2, **d1}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "we see that the value of **key2** is now **2**, since it was the second occurrence."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, we can unpack a dictionary into a dictionary as seen above, but we can mix in our own key-value pairs as well - it is just a dictionary literal after all."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 1, 'b': 2, 'c': 3, 'key1': 1, 'key2': 3, 'key3': 3}"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{'a': 1, 'b': 2, **d1, **d2, 'c':3}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, if we have the same keys, only the \"latest\" value of the key is retained:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'key1': 1, 'key2': 3, 'key3': 200}"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{'key1': 100, **d1, **d2, 'key3': 200}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Nested Unpacking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python even supports nested unpacking:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "X\n",
      "Y\n"
     ]
    }
   ],
   "source": [
    "a, b, (c, d) = [1, 2, ['X', 'Y']]\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact, since a string is an iterable, we can even write:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "X\n",
      "Y\n"
     ]
    }
   ],
   "source": [
    "a, b, (c, d) = [1, 2, 'XY']\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can even write something like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "p\n",
      "y\n",
      "['t', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "a, b, (c, d, *e) = [1, 2, 'python']\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remember when we said that we can use a * only **once**..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How about this then?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3]\n",
      "p\n",
      "y\n",
      "['t', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "a, *b, (c, d, *e) = [1, 2, 3, 'python']\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can break down what happened here in multiple steps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3]\n",
      "python\n"
     ]
    }
   ],
   "source": [
    "a, *b, tmp = [1, 2, 3, 'python']\n",
    "print(a)\n",
    "print(b)\n",
    "print(tmp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p\n",
      "y\n",
      "['t', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "c, d, *e = tmp\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So putting it together we get our original line of code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3]\n",
      "p\n",
      "y\n",
      "['t', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "a, *b, (c, d, *e) = [1, 2, 3, 'python']\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we wanted to do the same thing using slicing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, [2, 3], 'p', 'y', ['t', 'h', 'o', 'n'])"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l = [1, 2, 3, 'python']\n",
    "l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3]\n",
      "p\n",
      "y\n",
      "['t', 'h', 'o', 'n']\n"
     ]
    }
   ],
   "source": [
    "l = [1, 2, 3, 'python']\n",
    "a, b, c, d, e = l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, this works for arbitrary lengths and indexable sequence types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3, 4]\n",
      "u\n",
      "n\n",
      "['l', 'a', 'd', 'e', 'n', ' ', 's', 'w', 'a', 'l', 'l', 'o', 'w']\n"
     ]
    }
   ],
   "source": [
    "l = [1, 2, 3, 4, 'unladen swallow']\n",
    "a, b, c, d, e = l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or even:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[2, 3, 4]\n",
      "a\n",
      "b\n",
      "['c', 'd']\n"
     ]
    }
   ],
   "source": [
    "l = [1, 2, 3, 4, ['a', 'b', 'c', 'd']]\n",
    "a, b, c, d, e = l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])\n",
    "print(a)\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)\n",
    "print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
